using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Bookvar.ApplicationModel;
using Bookvar.ServiceModel;
using Bookvar.UI.Commands;

namespace Bookvar.UI.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        private Collection<CommandModel> commands;

        #region Properties

        public Collection<CommandModel> Commands
        {
            get
            {
                if ( commands == null )
                {
                    commands = new Collection<CommandModel>();
                }
                return commands;
            }
        }

        protected static IActionService ActionService
        {
            get
            {
                string mapKey = MindmapService.CurrentMindmap.Key.ToString();
                return BookvarApplication.Current.ServiceLayer.GetService<IActionService>( mapKey );
            }
        }

        protected static IAdornerService AdornerService
        {
            get { return BookvarApplication.Current.ServiceLayer.GetService<IAdornerService>(); }
        }

        protected static IViewService ViewService
        {
            get { return BookvarApplication.Current.ServiceLayer.GetService<IViewService>(); }
        }

        protected static IPersistenceService PersistenceService
        {
            get { return BookvarApplication.Current.ServiceLayer.GetService<IPersistenceService>(); }
        }

        protected static IMindmapService MindmapService
        {
            get { return BookvarApplication.Current.ServiceLayer.GetService<IMindmapService>(); }
        }

        protected static IErrorService ErrorService
        {
            get { return BookvarApplication.Current.ServiceLayer.GetService<IErrorService>(); }
        }

        public Collection<CommandBinding> CommandBindings
        {
            get
            {
                EnsureCommandsInitialized();

                var bindings = new Collection<CommandBinding>();

                foreach ( var command in Commands )
                {
                    bindings.Add( new CommandBinding( command.Command, command.OnExecute, command.OnCanExecute ) );
                }

                return bindings;
            }
        }

        public Collection<InputBinding> InputBindings
        {
            get
            {
                EnsureCommandsInitialized();

                var bindings = new Collection<InputBinding>();

                foreach ( var command in Commands.Where( c => c.HasInputGesture ) )
                {
                    bindings.Add( new InputBinding( command.Command, command.InputGesture ) );
                }

                return bindings;
            }
        }

        #endregion

        #region INotifyPropertyChanged implementation

        protected virtual void OnPropertyChanged( string propertyName )
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs( propertyName );
            if ( PropertyChanged != null )
            {
                PropertyChanged( this, e );
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        protected virtual void InitializeCommands()
        {
        }

        private void EnsureCommandsInitialized()
        {
            InitializeCommands();
        }

        [SuppressMessage( "Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#" )]
        protected void SetFieldValue<T>( string propertyName, ref T oldValue, T newValue )
        {
            if ( ( ( oldValue == null ) && ( newValue != null ) ) ||
                 ( ( oldValue != null ) && !oldValue.Equals( newValue ) ) )
            {
                oldValue = newValue;
                OnPropertyChanged( propertyName );
            }
        }

        public static void AddCommandAndInputBindings( UIElement element, ViewModelBase viewModel )
        {
            element.CommandBindings.AddRange( viewModel.CommandBindings );
            foreach ( var inputBinding in viewModel.InputBindings )
            {
                inputBinding.CommandTarget = element;
                element.InputBindings.Add( inputBinding );
            }
        }
    }
}